package com.atguigu.tingshu.account.service.impl;


import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.account.mapper.RechargeInfoMapper;
import com.atguigu.tingshu.account.mapper.UserAccountDetailMapper;
import com.atguigu.tingshu.account.mapper.UserAccountMapper;
import com.atguigu.tingshu.account.service.RechargeInfoService;
import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.IdWorker;
import com.atguigu.tingshu.model.account.RechargeInfo;
import com.atguigu.tingshu.model.account.UserAccount;
import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.payment.client.PaymentFeignClient;
import com.atguigu.tingshu.vo.account.RechargeInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Transactional(rollbackFor = Exception.class)
@SuppressWarnings({"unchecked", "rawtypes"})
public class RechargeInfoServiceImpl extends ServiceImpl<RechargeInfoMapper, RechargeInfo> implements RechargeInfoService {

	@Autowired
	private RechargeInfoMapper rechargeInfoMapper;
	@Autowired
	private RedisTemplate redisTemplate;
	@Autowired
	private UserAccountService userAccountService;
	@Autowired
	private UserAccountMapper userAccountMapper;
	@Autowired
	private UserAccountDetailMapper userAccountDetailMapper;
	@Autowired
	private IdWorker idWorker;
	@Autowired
	private RabbitTemplate rabbitTemplate;
	@Autowired
	private RedissonClient redissonClient;
	@Resource
	private PaymentFeignClient paymentFeignClient;

	/**
	 * 充值订单提交
	 * @param rechargeInfoVo
	 * @return
	 */
	@Override
	public Object submitRecharge(RechargeInfoVo rechargeInfoVo) {
		// 校验参数
		if (rechargeInfoVo == null){
			throw new GuiguException(201,"参数错误");
		}
		// 校验金额
		int value = rechargeInfoVo.getAmount().intValue();
		if (value <= 0 ){
			throw new GuiguException(201,"充值金额错误");
		}
		// 控制订单数量 -- 10分钟过期
		if (redisTemplate.opsForValue().setIfAbsent(
				"Recharge_Order_Lock_" + AuthContextHolder.getUserId(),
				value,
				10,
				TimeUnit.MINUTES)){
			try {
				// 查询账户信息
				UserAccount userAccount = userAccountService.getOne(
						new LambdaQueryWrapper<UserAccount>()
								.eq(UserAccount::getUserId, AuthContextHolder.getUserId()));
				// 计算充值后余额
				BigDecimal availableAmount = userAccount.getAvailableAmount();
				availableAmount.add(rechargeInfoVo.getAmount());
				// 金额上限校验
				if (availableAmount.intValue() > 200){
					throw new GuiguException(201,"充值金额超出200上限");
				}
				// 声明DO
				RechargeInfo rechargeInfo = new RechargeInfo();
				rechargeInfo.setUserId(AuthContextHolder.getUserId());
				rechargeInfo.setOrderNo("RechargeNo" + idWorker.nextId());
				rechargeInfo.setRechargeStatus("0901");
				rechargeInfo.setRechargeAmount(rechargeInfoVo.getAmount());
				rechargeInfo.setPayWay(rechargeInfoVo.getPayWay());
				save(rechargeInfo);
				// 返回结果 -- orderNo
				JSONObject result = new JSONObject();
				result.put("orderNo",rechargeInfo.getOrderNo());
				// 订单超时取消 -- rabbitmq
				rabbitTemplate.convertAndSend(
						"account_recharge_delay_exchange",
						"account.recharge.dead",
						rechargeInfo.getOrderNo() + ":" + rechargeInfo.getUserId(),
						message -> {
							MessageProperties messageProperties = message.getMessageProperties();
							messageProperties.setExpiration("600000");
							return message;
						});
				return result;
			}catch (Exception e){
				// 删锁 防止影响后续下单
				redisTemplate.delete("Recharge_Order_Lock_" + AuthContextHolder.getUserId());
				throw e;
			}
		}
		// 若订单重复则抛异常
		throw new GuiguException(201,"重复充值,存在一笔未支付充值订单");
	}

	/**
	 * 取消充值订单：1. 主动 2.超时(rabbitmq调用)
	 * @param orderNo
	 */
	@Override
	public void cancelRechargeInfo(String orderNo) {
		// 初始化取消状态：默认主动取消 -- "0903"
		String rechargeStatus = "0903";
		// 标识符 -- 取消订单后redis删除充值下单锁定
		Boolean flag = false;
		// 判断本地线程是否有userId -- 外部调用 or 内部调用
		Long userId = AuthContextHolder.getUserId();
		if (userId == null){
			// 内部调用 -- 超时取消 -- "0904"
			rechargeStatus = "0904";
			String[] split = orderNo.split(":");
			orderNo = split[0];
			userId = Long.valueOf(split[1]);
		}else {
			// 主动取消 -- 后续放锁 -- 超时取消redis已过期自动删除
			flag = true;
		}
		// 加锁 -- 防止取消与支付冲突
		RLock lock = redissonClient.getLock("Recharge_Pay_Cancel_Lock_" + orderNo + "_userId_" + userId);
		if (lock.tryLock()){
			try {
				// 查询指定订单：userId + orderNo + 未支付 -- "0901"
				RechargeInfo rechargeInfo = getOne(
						new LambdaQueryWrapper<RechargeInfo>()
								.eq(RechargeInfo::getOrderNo, orderNo)
								.eq(RechargeInfo::getUserId,userId)
								.eq(RechargeInfo::getRechargeStatus,"0901"));
				// 校验订单
				if (rechargeInfo != null){
					// 修改状态
					rechargeInfo.setRechargeStatus(rechargeStatus);
					// 写库
					updateById(rechargeInfo);
				}
			}catch (Exception e){
				throw e;
			}finally {
				// 释放锁 -- 取消与支付锁
				lock.unlock();
				// 释放锁 -- 控制下单数量 -- 不影响再次下单
				if (flag){
					redisTemplate.delete("Recharge_Order_Lock_" + AuthContextHolder.getUserId());
				}
			}
		}
	}


	/**
	 * 模拟余额支付返回支付地址
	 * @param orderNo
	 * @return
	 */
	@Override
	public Object getRechargePayInfo(String orderNo) {
		// 参数校验
		if (StringUtils.isEmpty(orderNo)){
			throw new GuiguException(201,"参数错误");
		}
		// 获取userId
		Long userId = AuthContextHolder.getUserId();
		// redis获取支付信息：防着重复申请支付地址
		String codeUrl = (String)redisTemplate.opsForValue().get("Recharge_PayUrl_User_" + userId + "_OrderNo_" + orderNo);
		if ( codeUrl != null){
			// 有 直接返回：防着重复申请支付地址
			return codeUrl;
		}
		// 没有 -- redisson获取锁：防着重复申请支付地址 -- 同一个订单只能有一个人申请
		RLock lock = redissonClient.getLock("Recharge_PayUrl_Lock_User_" + userId + "_OrderNo_" + orderNo);
		// 尝试加锁
		if(lock.tryLock()){
			try {
				// 查询订单信息
				RechargeInfo rechargeInfo = getOne(
						new LambdaQueryWrapper<RechargeInfo>()
								.eq(RechargeInfo::getOrderNo,orderNo)
								.eq(RechargeInfo::getUserId,userId)
								.eq(RechargeInfo::getRechargeStatus,"0901"));
				// 校验订单是否存在
				if (rechargeInfo == null){
					throw new GuiguException(201,"订单不存在");
				}
				// 申请参数拼接
				Map<String,String> params = new HashMap<>();
				params.put("description","用户充值" + rechargeInfo.getRechargeAmount() + "元");
				params.put("orderNo",orderNo);
				params.put("price",rechargeInfo.getRechargeAmount().toString());
				// 支付方式：1101-微信 1102-支付宝 1103-账户余额
				params.put("paymentType","1302");
				params.put("exchange","payment_info_exchange");
				params.put("routingKey","payment.account"); // 支付成功的场合 -- 给service-account发消息修改订单状态为已支付
				// paymentFeign调用发送请求获取url
				codeUrl = paymentFeignClient.getPayInfoFromWx(params);
				// 校验url是否存在
				if ( codeUrl != null){
					// 存在 存redis：防着重复申请支付地址 -- 10分钟内只能有一个支付地址申请
					redisTemplate.opsForValue().set(
							"Recharge_PayUrl_User_" + userId + "_OrderNo_" + orderNo,
							codeUrl,
							10,
							TimeUnit.MINUTES);
				}
			}catch (Exception e){
				throw e;
			}finally {
				// 放锁
				lock.unlock();
			}
		}
		// 返回支付连接
		return codeUrl;
	}

	/**
	 * 修改充值订单
	 * @param resultString
	 */
	@Override
	public void updateRechargeInfo(String resultString) {
		// 标记是否需要释放订单锁 -- 防止第二笔订单
		Boolean flag = false;
		// 参数反序列化
		Map<String,String> result = JSONObject.parseObject(resultString, Map.class);
		// 获取orderNo
		String orderNo = result.get("out_trade_no");
		// 查询订单
		RechargeInfo rechargeInfo = getOne(
				new LambdaQueryWrapper<RechargeInfo>()
						.eq(RechargeInfo::getOrderNo, orderNo));
		// 获取userId
		Long userId = rechargeInfo.getUserId();
		// 获取锁 -- 防止取消订单与支付订单冲突
		RLock lock = redissonClient.getLock("Recharge_Pay_Cancel_Lock_" + orderNo + "_userId_" + userId);
		// 加锁
		lock.lock();
		// 修改
		try{
			// 获取订单状态 -- "0902" 已付款
			String rechargeStatus = rechargeInfo.getRechargeStatus();
			if (!rechargeStatus.equals("0902")){
				rechargeInfo.setRechargeStatus("0902");
				updateById(rechargeInfo);
				// 添加账号余额
				userAccountMapper.addAvailableAmount(userId,rechargeInfo.getRechargeAmount().intValue());
				// 记录账号充值明细
				UserAccountDetail userAccountDetail = new UserAccountDetail();
				// 设置属性
				userAccountDetail.setUserId(userId);
				userAccountDetail.setTitle("用户充值" + rechargeInfo.getRechargeAmount() + "元");
				// 交易类型：1201-充值 1202-锁定 1203-解锁 1204-消费
				userAccountDetail.setTradeType("1201");
				userAccountDetail.setAmount(rechargeInfo.getRechargeAmount());
				userAccountDetail.setOrderNo(orderNo);
				// 账号充值明细写库
				userAccountDetailMapper.insert(userAccountDetail);
				// 充值订单锁标识位释放锁
				flag = true;
			}
		}catch (Exception e){
			throw e;
		}finally {
			// 放锁: 取消支付锁
			lock.unlock();
			// 放订单锁
			if (flag){
				redisTemplate.delete("Recharge_Order_Lock_" + AuthContextHolder.getUserId());
			}
		}
	}
}
